﻿using Microsoft.Azure.Documents.Client;
using Model.DocumentDB.Telemetry;
using Newtonsoft.Json;
using System;
using System.Configuration;
using System.IO;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using System.Web;

namespace Studio.DocumentDB.Api.Handlers
{
    /// <summary>Snapshot API.</summary>
    /// <remarks>Processes a snapshot JSON document (from HTTP body) and persists it to a DocumentDB collection.</remarks>
    public class SnapshotHandler : HttpTaskAsyncHandler
    {
        #region Inner Types
        private class ExtendedSnapshot : Snapshot
        {
            public DateTimeOffset TimeStamp { get; set; }

            public static void Extend(ExtendedSnapshot snapshot)
            {
                snapshot.TimeStamp = DateTimeOffset.Now;
                snapshot.ReleaseVersion = ExtendedReleaseVersion.Extend(snapshot.ReleaseVersion);
            }
        }

        private class ExtendedReleaseVersion : ReleaseVersion
        {
            public int Major { get; set; }
            public int Minor { get; set; }
            public int Build { get; set; }
            public int Revision { get; set; }
            public string Error { get; set; }

            public static ExtendedReleaseVersion Extend(ReleaseVersion releaseVersion)
            {
                try
                {
                    var version = new Version(releaseVersion.TextVersion);

                    return new ExtendedReleaseVersion
                    {
                        TextVersion = version.ToString(),
                        Major = version.Major,
                        Minor = version.Minor,
                        Build = version.Build,
                        Revision = version.Revision
                    };
                }
                catch (Exception ex)
                {
                    return new ExtendedReleaseVersion
                    {
                        TextVersion = "Corrupted Version",
                        Error = releaseVersion.TextVersion + " ; " + ex.Message
                    };
                }
            }
        }
        #endregion

        private const string USER_ID = "UserID";
        private const string SESSION_ID = "SessionID";
        private const string RELEASE_VERSION = "ReleaseVersion";

        private static readonly Lazy<DocumentClient> _lazyClient = new Lazy<DocumentClient>(
            CreateClient,
            LazyThreadSafetyMode.ExecutionAndPublication);

        /// <summary>Process the PUT request.</summary>
        /// <param name="context">Request context.</param>
        /// <returns>Asynchronous completion.</returns>
        public override async Task ProcessRequestAsync(HttpContext context)
        {
            using (var stream = context.Request.GetBufferedInputStream())
            using (var requestReader = new StreamReader(stream))
            {
                var authorizationHeader = context.Request.Headers[HttpRequestHeader.Authorization.ToString()];
                var requestPayload = await requestReader.ReadToEndAsync().ConfigureAwait(false);
                var snapshot = JsonConvert.DeserializeObject<ExtendedSnapshot>(requestPayload);

                if (!IsAuthorized(snapshot, authorizationHeader))
                {
                    context.Response.StatusCode = (int)HttpStatusCode.Forbidden;
                }
                else if (!IsValidPayload(snapshot))
                {
                    context.Response.StatusCode = (int)HttpStatusCode.BadRequest;
                }
                else
                {
                    ExtendedSnapshot.Extend(snapshot);

                    await PushPayload(snapshot).ConfigureAwait(false);
                    context.Response.StatusCode = (int)HttpStatusCode.Accepted;
                }
            }
        }

        private bool IsAuthorized(Snapshot snapshot, string authorizationHeader)
        {
            return snapshot != null
                && snapshot.UserID != null
                && snapshot.SessionID != null
                && authorizationHeader == snapshot.UserID + snapshot.SessionID;
        }

        private static bool IsValidPayload(Snapshot snapshot)
        {
            return true;
        }

        private Task PushPayload(object snapshot)
        {
            var documentCollectionLink = ConfigurationManager.AppSettings["documentCollectionLink"];

            if (string.IsNullOrWhiteSpace(documentCollectionLink))
            {
                throw new ConfigurationErrorsException("Config keys are missing");
            }

            return _lazyClient.Value.CreateDocumentAsync(documentCollectionLink, snapshot);
        }

        private static DocumentClient CreateClient()
        {
            var endpoint = ConfigurationManager.AppSettings["endpoint"];
            var authKey = ConfigurationManager.AppSettings["authKey"];

            if (string.IsNullOrWhiteSpace(endpoint) || string.IsNullOrWhiteSpace(authKey))
            {
                throw new ConfigurationErrorsException("Config keys are missing");
            }

            return new DocumentClient(new Uri(endpoint), authKey);
        }
    }
}